package com.hyacinth.lotterysystem.service.impl;

import cn.hutool.core.lang.Validator;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hyacinth.lotterysystem.common.constant.Constants;
import com.hyacinth.lotterysystem.common.errocde.ServiceErrorCodeConstants;
import com.hyacinth.lotterysystem.common.exception.ServiceException;
import com.hyacinth.lotterysystem.common.utils.JWTUtil;
import com.hyacinth.lotterysystem.common.utils.RedisUtil;
import com.hyacinth.lotterysystem.dao.entity.UserDO;
import com.hyacinth.lotterysystem.dao.handler.Encrypt;
import com.hyacinth.lotterysystem.dao.mapper.UserMapper;
import com.hyacinth.lotterysystem.dto.req.UserEmailLoginReq;
import com.hyacinth.lotterysystem.dto.req.UserPasswordLoginReq;
import com.hyacinth.lotterysystem.dto.req.UserRegisterReq;
import com.hyacinth.lotterysystem.dto.resp.UserInfoListResp;
import com.hyacinth.lotterysystem.dto.resp.UserLoginResp;
import com.hyacinth.lotterysystem.dto.resp.UserRegisterResp;
import com.hyacinth.lotterysystem.service.UserService;
import com.hyacinth.lotterysystem.service.enums.UserIdentityEnum;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisUtil redisUtil;

    @Override
    public UserRegisterResp register(UserRegisterReq param) {
        checkUserRegisterParam(param);
        param.setPassword(DigestUtil.sha256Hex(param.getPassword()));
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(param, userDO);
        if (param.getPhoneNumber() != null)
            userDO.setPhoneNumber(new Encrypt(param.getPhoneNumber()));

        int userId = userMapper.insert(userDO);
        return new UserRegisterResp(userId);
    }

    @Override
    public UserLoginResp login(UserPasswordLoginReq param) {
        // 校验手机号码
        if (!Validator.isMobile(param.getPhoneNumber()))
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);

        // 查询对应的用户
        LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class)
                .eq(UserDO::getPhoneNumber, new Encrypt(param.getPhoneNumber()));
        UserDO userDO = userMapper.selectOne(queryWrapper);
        if (userDO == null)
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXISTS);

        // 如果用户信息不为空 &&  但用户登录身份信息和数据库中查询的用户不一致
        if (StringUtils.hasText(param.getMandatorIdentity()) &&
                !param.getMandatorIdentity().equalsIgnoreCase(userDO.getIdentity()))
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);

        if (!DigestUtil.sha256Hex(param.getPassword()).equals(userDO.getPassword()))
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_SHORT_ERROR);

        Map<String, Object> map = new HashMap<>();
        map.put("id", userDO.getId());
        String token = JWTUtil.genJwt(map);
        return new UserLoginResp(token, UserIdentityEnum.forName(userDO.getIdentity()));
    }

    @Override
    public UserLoginResp login(UserEmailLoginReq param) {
        // 判断邮箱格式
        if (!Validator.isEmail(param.getEmail()))
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);

        // 判断验证码是否输入正确
        String code = redisUtil.get(Constants.REDIS_KEY_EMAIL_CODE_PREFIX + param.getEmail());
        if (!param.getVerificationCode().equalsIgnoreCase(code))
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);

        // 查询用户
        LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class)
                .eq(UserDO::getEmail, param.getEmail());
        UserDO userDO = userMapper.selectOne(queryWrapper);
        if (userDO == null)
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXISTS);

        // 校验身份信息
        if (StringUtils.hasText(param.getMandatorIdentity())
                && !param.getMandatorIdentity().equals(userDO.getIdentity()))
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);

        Map<String, Object> map = new HashMap<>();
        map.put("id", userDO.getId());
        String token = JWTUtil.genJwt(map);
        return new UserLoginResp(token, UserIdentityEnum.forName(userDO.getIdentity()));
    }

    @Override
    public List<UserInfoListResp> selectUserListByIdentity(UserIdentityEnum userIdentityEnum) {
        String identity = userIdentityEnum == null ? null : userIdentityEnum.name();
        LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class)
                .orderByDesc(UserDO::getId);
        if (identity != null) {
            queryWrapper.eq(UserDO::getIdentity, identity);
        }
        List<UserDO> userDOList = userMapper.selectList(queryWrapper);
        return userDOList.stream()
                .map(userDO -> {
                            UserInfoListResp resp = new UserInfoListResp();
                            BeanUtils.copyProperties(userDO, resp);
                            return resp;
                        }
                ).toList();
    }


    private void checkUserRegisterParam(UserRegisterReq param) {
        // 校验参数是否为空
        if (param == null)
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);

        // 校验用户身份信息
        if (UserIdentityEnum.forName(param.getIdentity()) == null)
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);

        // 管理员必填密码 且校验密码长度 长度>=6
        if (param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name()) &&
                (!StringUtils.hasText(param.getPassword()) || param.getPassword().length() < 6))
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_SHORT_ERROR);


        // hutool 校验邮箱
        if (!StringUtils.hasText(param.getEmail()) || !Validator.isEmail(param.getEmail()))
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);

        // 检查邮箱是否被使用

        if (checkMailUsed(param.getEmail())) throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);

        // 校验手机号
        if (!StringUtils.hasText(param.getPhoneNumber()) || !Validator.isMobile(param.getPhoneNumber()))
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);


        if (checkPhoneNumberUsed(param.getPhoneNumber()))
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
    }

    private boolean checkMailUsed(String mail) {
        return userMapper.countByMail(mail) == 1;
    }

    private boolean checkPhoneNumberUsed(String phoneNumber) {
        return userMapper.countByPhoneNumber(new Encrypt(phoneNumber)) == 1;
    }
}
